// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011-2018 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef EIGEN_TEST_ANNOYING_SCALAR_H
#define EIGEN_TEST_ANNOYING_SCALAR_H

#include <ostream>

#if EIGEN_COMP_GNUC
#pragma GCC diagnostic ignored "-Wshadow"
#endif

#ifndef EIGEN_TEST_ANNOYING_SCALAR_DONT_THROW
struct my_exception
{
	my_exception() {}
	~my_exception() {}
};
#endif

// An AnnoyingScalar is a pseudo scalar type that:
// - can randomly through an exception in operator +
// - randomly allocate on the heap or initialize a reference to itself making it non trivially copyable, nor movable,
// nor relocatable.

class AnnoyingScalar
{
  public:
	AnnoyingScalar()
	{
		init();
		*v = 0;
	}
	AnnoyingScalar(long double _v)
	{
		init();
		*v = _v;
	}
	AnnoyingScalar(double _v)
	{
		init();
		*v = _v;
	}
	AnnoyingScalar(float _v)
	{
		init();
		*v = _v;
	}
	AnnoyingScalar(int _v)
	{
		init();
		*v = _v;
	}
	AnnoyingScalar(long _v)
	{
		init();
		*v = _v;
	}
#if EIGEN_HAS_CXX11
	AnnoyingScalar(long long _v)
	{
		init();
		*v = _v;
	}
#endif
	AnnoyingScalar(const AnnoyingScalar& other)
	{
		init();
		*v = *(other.v);
	}
	~AnnoyingScalar()
	{
		if (v != &data)
			delete v;
		instances--;
	}

	void init()
	{
		if (internal::random<bool>())
			v = new float;
		else
			v = &data;
		instances++;
	}

	AnnoyingScalar operator+(const AnnoyingScalar& other) const
	{
#ifndef EIGEN_TEST_ANNOYING_SCALAR_DONT_THROW
		countdown--;
		if (countdown <= 0 && !dont_throw)
			throw my_exception();
#endif
		return AnnoyingScalar(*v + *other.v);
	}

	AnnoyingScalar operator-() const { return AnnoyingScalar(-*v); }

	AnnoyingScalar operator-(const AnnoyingScalar& other) const { return AnnoyingScalar(*v - *other.v); }

	AnnoyingScalar operator*(const AnnoyingScalar& other) const { return AnnoyingScalar((*v) * (*other.v)); }

	AnnoyingScalar operator/(const AnnoyingScalar& other) const { return AnnoyingScalar((*v) / (*other.v)); }

	AnnoyingScalar& operator+=(const AnnoyingScalar& other)
	{
		*v += *other.v;
		return *this;
	}
	AnnoyingScalar& operator-=(const AnnoyingScalar& other)
	{
		*v -= *other.v;
		return *this;
	}
	AnnoyingScalar& operator*=(const AnnoyingScalar& other)
	{
		*v *= *other.v;
		return *this;
	}
	AnnoyingScalar& operator/=(const AnnoyingScalar& other)
	{
		*v /= *other.v;
		return *this;
	}
	AnnoyingScalar& operator=(const AnnoyingScalar& other)
	{
		*v = *other.v;
		return *this;
	}

	bool operator==(const AnnoyingScalar& other) const { return *v == *other.v; }
	bool operator!=(const AnnoyingScalar& other) const { return *v != *other.v; }
	bool operator<=(const AnnoyingScalar& other) const { return *v <= *other.v; }
	bool operator<(const AnnoyingScalar& other) const { return *v < *other.v; }
	bool operator>=(const AnnoyingScalar& other) const { return *v >= *other.v; }
	bool operator>(const AnnoyingScalar& other) const { return *v > *other.v; }

	float* v;
	float data;
	static int instances;
#ifndef EIGEN_TEST_ANNOYING_SCALAR_DONT_THROW
	static int countdown;
	static bool dont_throw;
#endif
};

AnnoyingScalar
real(const AnnoyingScalar& x)
{
	return x;
}
AnnoyingScalar
imag(const AnnoyingScalar&)
{
	return 0;
}
AnnoyingScalar
conj(const AnnoyingScalar& x)
{
	return x;
}
AnnoyingScalar
sqrt(const AnnoyingScalar& x)
{
	return std::sqrt(*x.v);
}
AnnoyingScalar
abs(const AnnoyingScalar& x)
{
	return std::abs(*x.v);
}
AnnoyingScalar
cos(const AnnoyingScalar& x)
{
	return std::cos(*x.v);
}
AnnoyingScalar
sin(const AnnoyingScalar& x)
{
	return std::sin(*x.v);
}
AnnoyingScalar
acos(const AnnoyingScalar& x)
{
	return std::acos(*x.v);
}
AnnoyingScalar
atan2(const AnnoyingScalar& y, const AnnoyingScalar& x)
{
	return std::atan2(*y.v, *x.v);
}

std::ostream&
operator<<(std::ostream& stream, const AnnoyingScalar& x)
{
	stream << (*(x.v));
	return stream;
}

int AnnoyingScalar::instances = 0;

#ifndef EIGEN_TEST_ANNOYING_SCALAR_DONT_THROW
int AnnoyingScalar::countdown = 0;
bool AnnoyingScalar::dont_throw = false;
#endif

namespace Eigen {
template<>
struct NumTraits<AnnoyingScalar> : NumTraits<float>
{
	enum
	{
		RequireInitialization = 1,
	};
	typedef AnnoyingScalar Real;
	typedef AnnoyingScalar Nested;
	typedef AnnoyingScalar Literal;
	typedef AnnoyingScalar NonInteger;
};

template<>
inline AnnoyingScalar
test_precision<AnnoyingScalar>()
{
	return test_precision<float>();
}

namespace numext {
template<>
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool(isfinite)(const AnnoyingScalar& x)
{
	return (numext::isfinite)(*x.v);
}
}

namespace internal {
template<>
EIGEN_STRONG_INLINE double
cast(const AnnoyingScalar& x)
{
	return double(*x.v);
}
template<>
EIGEN_STRONG_INLINE float
cast(const AnnoyingScalar& x)
{
	return *x.v;
}
}
} // namespace Eigen

AnnoyingScalar
get_test_precision(const AnnoyingScalar&)
{
	return Eigen::test_precision<AnnoyingScalar>();
}

AnnoyingScalar
test_relative_error(const AnnoyingScalar& a, const AnnoyingScalar& b)
{
	return test_relative_error(*a.v, *b.v);
}

inline bool
test_isApprox(const AnnoyingScalar& a, const AnnoyingScalar& b)
{
	return internal::isApprox(*a.v, *b.v, test_precision<float>());
}

inline bool
test_isMuchSmallerThan(const AnnoyingScalar& a, const AnnoyingScalar& b)
{
	return test_isMuchSmallerThan(*a.v, *b.v);
}

#endif // EIGEN_TEST_ANNOYING_SCALAR_H
